package com.mario.repository;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;

import com.mario.domain.MySerializable;

public class GenericDAO<T extends MySerializable> extends HibernateDaoSupport implements Dao<T> {
	
	/** Logger for this class and subclasses */
	protected final static Logger logger = Logger.getLogger(GenericDAO.class);
	
	//*******************************************************************
	// Attributes
	//*******************************************************************
	
	private Class<T> domainClass = getDomainClass();
 
	//*******************************************************************
	// Constructor
	//*******************************************************************
	
	public GenericDAO(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}
	
	//*******************************************************************
	// Methods
	//*******************************************************************
	
	@Transactional
	public long persist(T t) throws HibernateException {
		long oid;
		
	 	try {
	 		logger.info("Saving " + domainClass.getSimpleName() + " - ID " + t.getOid());
	 		getHibernateTemplate().persist(t);
	 		oid = t.getOid();
	 	} catch (DataAccessException ex) {
	 		logger.error("Error Saving " + domainClass.getSimpleName(), ex);
	 		throw ex;
	 	}
	 	
	 	return oid;
	}

	@Transactional 
	public void update(T t) throws HibernateException{
		try {
			logger.info("Updating " + domainClass.getSimpleName() + " - ID " + t.getOid());
			getHibernateTemplate().update(t);
	 	} catch (DataAccessException ex) {
	 		logger.error("Error Updating " + domainClass.getSimpleName(), ex);
	 		throw ex;
		}
	}
	
	@Transactional 
	public T load(Long id) {
		try {
			logger.info("Loading " + domainClass.getSimpleName() + " - ID " + id);
			T returnValue = (T) getHibernateTemplate().load(domainClass, id.longValue());
			return returnValue;
		} catch (DataAccessException ex) {
			logger.error(domainClass.getSimpleName() + " with ID = " + id + " not found.", ex);
			return null;
		}
	}	
 
	@Transactional 
	public List<T> getAll() {
		try {
			logger.info("Getting " + domainClass.getSimpleName() + " List.");
			return new ArrayList<T>(getHibernateTemplate().loadAll(domainClass));
		} catch (DataAccessException ex) {
			logger.error("Error getting " + domainClass.getSimpleName() + " List.", ex);
			return null;
		}
	}
 
	@Transactional 
	public void deleteById(Long id) {
		T t;
		try {
			t = this.load(id);
		} catch (ObjectNotFoundException e) {
			return;
		}
		
		delete(t);
	}
	
	@Transactional 
	public void delete(T t) {
		try {
			logger.info("Deleting " + domainClass.getSimpleName() + " - ID " + t.getOid());
			getHibernateTemplate().delete(t);
		} catch (DataAccessException ex) {
			logger.error("Error deleting " + domainClass.getSimpleName(), ex);
		}
		
	}
 
	@Transactional 
	public int deleteAll() {
		try {
			logger.info("Deleting all " + domainClass.getSimpleName());
			List<T> list = getAll();
			getHibernateTemplate().deleteAll(list);
			return list.size();
		} catch (DataAccessException ex) {
			logger.error("Error deleting all " + domainClass.getSimpleName(), ex);
			return 0;
		}
	}
 
	@Transactional 
	public int count() {
		try {
			logger.info("Counting " + domainClass.getSimpleName());
			return getAll().size();
		} catch (DataAccessException ex) {
			logger.error("Error counting " + domainClass.getSimpleName(), ex);
			return 0;
		}
	}
 
	@Transactional 
	public List<T> findByExample(T exampleObject) {
	 	try {
	 		logger.info("Loading " + domainClass.getSimpleName() + " by example.");
	 		return getHibernateTemplate().findByExample(exampleObject);
		} catch (Exception ex) {
			logger.error("Error loading " + domainClass.getSimpleName() + " by example.", ex);
			return null;
		}
	}
	
	protected Class<T> getDomainClass() {
		if (domainClass != null)
			return this.domainClass;
		
		ParameterizedType thisType;
		thisType = (ParameterizedType) getClass().getGenericSuperclass();
		return (Class<T>) thisType.getActualTypeArguments()[0];
	}
}
